/*------------------------------------------------------------------------------
 * MDK Middleware - Component ::USB:Device
 * Copyright (c) 2004-2017 ARM Germany GmbH. All rights reserved.
 *------------------------------------------------------------------------------
 * Name:    USBD_User_HID_0.c
 * Purpose: USB Device Human Interface Device class (HID) User module
 * Rev.:    V6.2.3
 *----------------------------------------------------------------------------*/
/**
 * \addtogroup usbd_hidFunctions
 *
 * USBD_User_HID_0.c implements the application specific functionality of the
 * HID class and is used to receive and send data reports to the USB Host.
 *
 * The implementation must match the configuration file USBD_Config_HID_0.h.
 * The following values in USBD_Config_HID_0.h affect the user code:
 *
 *  - 'Endpoint polling Interval' specifies the frequency of requests
 *    initiated by USB Host for \ref USBD_HIDn_GetReport.
 *
 *  - 'Number of Output Reports' configures the values for \em rid of
 *    \ref USBD_HIDn_SetReport.
 *
 *  - 'Number of Input Reports' configures the values for \em rid of
 *    \ref USBD_HIDn_GetReport and \ref USBD_HID_GetReportTrigger.
 *
 *  - 'Maximum Input Report Size' specifies the maximum value for:
 *       - return of \ref USBD_HIDn_GetReport
 *       - len of \ref USBD_HID_GetReportTrigger.
 *
 *  - 'Maximum Output Report Size' specifies the maximum value for \em len
 *    in \ref USBD_HIDn_SetReport for rtype=HID_REPORT_OUTPUT
 *
 *  - 'Maximum Feature Report Size' specifies the maximum value for \em len
 *    in \ref USBD_HIDn_SetReport for rtype=HID_REPORT_FEATURE
 *
 */

//! [code_USBD_User_HID]

#include <stdbool.h>
#include <stdint.h>
#include "DAP.h"
#include "DAP_config.h"
#include "cherryusb_main.h"

static volatile uint16_t USB_RequestIndexI = 0; // Request  Index In
static volatile uint16_t USB_RequestIndexO = 0; // Request  Index Out
static volatile uint16_t USB_RequestCountI = 0; // Request  Count In
static volatile uint16_t USB_RequestCountO = 0; // Request  Count Out

static volatile uint16_t USB_ResponseIndexI = 0; // Response Index In
static volatile uint16_t USB_ResponseIndexO = 0; // Response Index Out
static volatile uint16_t USB_ResponseCountI = 0; // Response Count In
static volatile uint16_t USB_ResponseCountO = 0; // Response Count Out
static volatile uint16_t USB_ResponseIdle = 1;   // Response Idle  Flag

static volatile uint32_t USB_EventFlags = 0;     // Response Idle  Flag

static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t USB_Request[DAP_PACKET_COUNT][DAP_PACKET_SIZE];  // Request  Buffer
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t USB_Response[DAP_PACKET_COUNT][DAP_PACKET_SIZE]; // Response Buffer
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint32_t USB_ResponseCount[DAP_PACKET_COUNT];            // Response Data Count

void usbd_dap_configured(void)
{
	usbd_ep_start_read(USB_BUS_ID, DAP_OUT_EP, USB_Request[USB_RequestIndexI], DAP_PACKET_SIZE);  // Store received data into request buffer
}

// Called during USBD_Initialize to initialize the USB HID class instance.
void USBD_HID0_Initialize(void)
{
	// Initialize variables
	USB_RequestIndexI = 0U;
	USB_RequestIndexO = 0U;
	USB_RequestCountI = 0U;
	USB_RequestCountO = 0U;
	USB_ResponseIndexI = 0U;
	USB_ResponseIndexO = 0U;
	USB_ResponseCountI = 0U;
	USB_ResponseCountO = 0U;
	USB_ResponseIdle = 1U;
	USB_EventFlags = 0U;
}

// Called during USBD_Uninitialize to de-initialize the USB HID class instance.
void USBD_HID0_Uninitialize(void) {}

void dap_ep_in_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
#ifdef USE_WINUSB
	if((nbytes % DAP_PACKET_SIZE) == 0 && nbytes)
	{
		usbd_ep_start_write(busid, ep, NULL, 0); // send zlp
		return;
	}
#endif
	if(USB_ResponseCountI != USB_ResponseCountO)
	{
#ifdef USE_WINUSB
		usbd_ep_start_write(USB_BUS_ID, DAP_IN_EP, USB_Response[USB_ResponseIndexO], USB_ResponseCount[USB_ResponseIndexO]); // Load data from response buffer to be sent back
#else
		usbd_ep_start_write(USB_BUS_ID, DAP_IN_EP, USB_Response[USB_ResponseIndexO], DAP_PACKET_SIZE); // Load data from response buffer to be sent back
#endif
		USB_ResponseIndexO++;
		if(USB_ResponseIndexO == DAP_PACKET_COUNT)
		{
			USB_ResponseIndexO = 0U;
		}
		USB_ResponseCountO++;
	}
	else
	{
		USB_ResponseIdle = 1U;
	}
}

void dap_ep_out_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
	(void)busid;
	(void)ep;
	(void)nbytes;
#if 0
	printf(">>> ");
	for(uint32_t i = 0; i < nbytes; i++)
	{
		printf("%02X ", USB_Request[USB_RequestIndexI][i]);
	}
	printf("\r\n");
#endif
	if(USB_Request[USB_RequestIndexI][0] == ID_DAP_TransferAbort)
	{
		DAP_TransferAbort = 1U;
	}
	else
	{
		USB_RequestIndexI++;
		if(USB_RequestIndexI == DAP_PACKET_COUNT)
		{
			USB_RequestIndexI = 0U;
		}
		USB_RequestCountI++;
		USB_EventFlags |= 0x01;
	}
	if((uint16_t)(USB_RequestCountI - USB_RequestCountO) == DAP_PACKET_COUNT)
	{
		USB_EventFlags |= 0x80;  // Discard packet when buffer is full
	}else {
		usbd_ep_start_read(USB_BUS_ID, DAP_OUT_EP, USB_Request[USB_RequestIndexI], DAP_PACKET_SIZE);  // Store received data into request buffer
	}
}

extern void cherryusb_handler(void);

void DAP_Thread(void *argument)
{
	uint32_t n, flags;
	(void)argument;
	for(;;)
	{
		while((USB_EventFlags & 0x01) == 0)
		{
			cherryusb_handler();
		}
		USB_EventFlags &= (~0x01);

		// Process pending requests
		while(USB_RequestCountI != USB_RequestCountO)
		{
			// Handle Queue Commands
			n = USB_RequestIndexO;
			while(USB_Request[n][0] == ID_DAP_QueueCommands)
			{
				USB_Request[n][0] = ID_DAP_ExecuteCommands;
				n++;
				if(n == DAP_PACKET_COUNT)
				{
					n = 0U;
				}
				if(n == USB_RequestIndexI)
				{
					while((USB_EventFlags & 0x81) == 0)
					{
						cherryusb_handler();
					}
					flags = USB_EventFlags & 0x80;
					USB_EventFlags &= (~0x81);
					if(flags & 0x80U)
					{
						break;
					}
				}
			}

			// Execute DAP Command (process request and prepare response)
			USB_ResponseCount[USB_ResponseIndexI] = DAP_ExecuteCommand(USB_Request[USB_RequestIndexO], USB_Response[USB_ResponseIndexI]) & 0xFFFF;

			// Update Request Index and Count
			USB_RequestIndexO++;
			if(USB_RequestIndexO == DAP_PACKET_COUNT)
			{
				USB_RequestIndexO = 0U;
			}
			USB_RequestCountO++;

			// Update Response Index and Count
			USB_ResponseIndexI++;
			if(USB_ResponseIndexI == DAP_PACKET_COUNT)
			{
				USB_ResponseIndexI = 0U;
			}
			USB_ResponseCountI++;

			if(USB_ResponseIdle)
			{
				if(USB_ResponseCountI != USB_ResponseCountO)
				{
					// Load data from response buffer to be sent back
					n = USB_ResponseIndexO++;
					if(USB_ResponseIndexO == DAP_PACKET_COUNT)
					{
						USB_ResponseIndexO = 0U;
					}
					USB_ResponseCountO++;
					USB_ResponseIdle = 0U;
#ifdef USE_WINUSB
					usbd_ep_start_write(USB_BUS_ID, DAP_IN_EP, USB_Response[n], USB_ResponseCount[n]);
#else
					usbd_ep_start_write(USB_BUS_ID, DAP_IN_EP, USB_Response[n], DAP_PACKET_SIZE);
#endif
#if 0
					printf("<<< ");
					for(uint32_t i = 0; i < USB_ResponseCount[n]; ++)
					{
						printf("%02X ", USB_Response[n][i]);
					}
					printf("\r\n");
#endif
				}
			}

			if(flags & 0x80)
			{
				flags &= ~0x80;
				usbd_ep_start_read(USB_BUS_ID, DAP_OUT_EP, USB_Request[USB_RequestIndexI], DAP_PACKET_SIZE);  // Store received data into request buffer
			}
		}
	}
}

//! [code_USBD_User_HID]
